package stackElimination;

import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicStampedReference;

public class AsymmetricRendevousStack<T> extends LockFreeStack<T> implements MyStack<T> {
	private EliminationArray<T> eliminationArray;
	private static ThreadLocal<RangePolicy> policy;
	private int exchangerCapacity, exchangerWaitDuration;

	public AsymmetricRendevousStack(final int exchangerCapacity, int exchangerWaitDuration) {
		super(0, 0);
		this.exchangerCapacity = exchangerCapacity;
		this.exchangerWaitDuration = exchangerWaitDuration;
		eliminationArray = new EliminationArray<T>(exchangerCapacity, exchangerWaitDuration);
		policy = new ThreadLocal<RangePolicy>() {
			protected synchronized RangePolicy initialValue() {
				return new RangePolicy(exchangerCapacity);
			}
		};
	}

	public String getStackInfo() {
		return exchangerCapacity + "," + exchangerWaitDuration;
	}

	public void push(T value) {
		RangePolicy rangePolicy = policy.get();
		Node<T> node = new Node<T>(value);

		while (true) {
			if (this.tryPush(node))
				return;
			else {
				try {
					T otherValue = eliminationArray.visit(value, rangePolicy.getRange());
					if (otherValue == null) {
						rangePolicy.recordEliminationSuccess();
						return;
					}
				} catch (TimeoutException e) {
					//System.out.println("Elimination Timed-out . Trying normal push");
					rangePolicy.recordEliminationTimeout();
				}
			}
		}
	}

	public T pop() throws Exception {
		RangePolicy rangePolicy = policy.get();

		while (true) {
			Node<T> returnNode = tryPop();
			if (returnNode != null)
				return returnNode.value;
			else {
				try {
					T otherValue = eliminationArray.visit(null, rangePolicy.getRange());
					if (otherValue != null) {
						rangePolicy.recordEliminationSuccess();
						return otherValue;
					}
				} catch (TimeoutException e) {
					//System.out.println("Elimination Timed-out . Trying normal pop");
					rangePolicy.recordEliminationTimeout();
				}
			}
		}
	}
}